﻿using System;
using System.Collections.Generic;
using System.Linq;
using DM4;
using System.Text.RegularExpressions;
using OpenPop.Mime;
using OpenPop.Mime.Header;
using OpenPop.Pop3;
using sscore;

namespace DMN_MailListener
{
    internal class MailChecker
    {
        private readonly MailListenerResultHandler _onChekerResult;
        private readonly TaskParams _tprm;

        public MailChecker(MailListenerResultHandler onChekerResult, TaskParams tprm)
        {
            _onChekerResult = onChekerResult;
            _tprm = tprm;
        }

        public virtual void DoWork()
        {
            WriteLog(String.Format("Start check mail [{0}]", _tprm.MailParams.MailAccountForCheck.Mail));

            try
            {
                using (Pop3Client client = new Pop3Client())
                {
                    client.Connect(_tprm.MailParams.MailAccountForCheck.ReceiveServer, Convert.ToInt32(_tprm.MailParams.MailAccountForCheck.ReceivePort), _tprm.MailParams.MailAccountForCheck.ReceiveUseSSL);
                    // Authenticate ourselves towards the server
                    client.Authenticate(_tprm.MailParams.MailAccountForCheck.ReceiveUser, _tprm.MailParams.MailAccountForCheck.ReceivePassword);
                    int messageCount = client.GetMessageCount();


                    for (int iMessage = 1; iMessage <= messageCount; iMessage++)   // Messages are numbered in the interval: [1, messageCount].  Ergo: message numbers are 1-based.
                    {
                        for (int iLetters = 0; iLetters < _tprm.MailParams.LettersWorkParam.Count; iLetters++)
                        {
                            LetterWorkParameters lwp = _tprm.MailParams.LettersWorkParam[iLetters];
                            Message m = null; byte[] mContent = null;

                            if (lwp.Result != Consts.TRUE)
                            {
                                try
                                {
                                    bool needCheckSender = lwp.Sender.Id != DM4Consts.DefaultNULLId;
                                    bool SenderIsMatched = false;
                                    if (lwp.Sender.Id != DM4Consts.DefaultNULLId)
                                    {
                                        MessageHeader headers = client.GetMessageHeaders(iMessage);
                                        RfcMailAddress from = headers.From;
                                        if (from.HasValidMailAddress && from.Address.ToLower().Contains(lwp.Sender.Mail.ToLower()))
                                            SenderIsMatched = true;
                                    }

                                    if ((!needCheckSender || SenderIsMatched)//если проверять Sender было не нужно или прошла проверка по Sender
                                        && IsNeedCheckRule(lwp.MailRules, MailRuleType.CheckSubject))//если нужно проверять Subject письма
                                    {
                                        CheckSubject(lwp.MailRules, client, iMessage, MailRuleType.CheckSubject);
                                    }

                                    if ((!needCheckSender || SenderIsMatched) //если проверять Sender было не нужно или прошла проверка по Sender
                                        && (!IsNeedCheckRule(lwp.MailRules, MailRuleType.CheckSubject)
                                            || IsMatchExists(lwp.MailRules, MailRuleType.CheckSubject))//если проверять Subject было не нужно или прошла проверка по Subject
                                        && IsNeedCheckRule(lwp.MailRules, MailRuleType.CheckBody)) //если нужно проверять тело письма
                                    {
                                        GetMessage(client, iMessage, ref mContent, ref m);
                                        CheckBody(lwp.MailRules, m, MailRuleType.CheckBody);
                                    }

                                    // если прошли проверку (нашли нужное письмо)
                                    if ((!needCheckSender || SenderIsMatched) //если проверять Sender было не нужно или прошла проверка по Sender
                                        && (!IsNeedCheckRule(lwp.MailRules, MailRuleType.CheckSubject)
                                            || IsMatchExists(lwp.MailRules, MailRuleType.CheckSubject))//если проверять Subject было не нужно или прошла проверка по Subject
                                        && (!IsNeedCheckRule(lwp.MailRules, MailRuleType.CheckBody)
                                            || IsMatchExists(lwp.MailRules, MailRuleType.CheckBody))) //если нужно проверять тело письма
                                    {
                                        GetMessage(client, iMessage, ref mContent, ref m);
                                        if (CheckBody(lwp.MailRules, m, MailRuleType.GetLink))
                                        {
                                            string user = "", passw = "";
                                            CheckBody(lwp.MailRules, m, MailRuleType.GetUser);
                                            FirstMatch(lwp.MailRules, MailRuleType.GetUser, out user);
                                            CheckBody(lwp.MailRules, m, MailRuleType.GetPassword);
                                            FirstMatch(lwp.MailRules, MailRuleType.GetPassword, out passw);

                                            foreach (MailRuleResult mrr in lwp.MailRules)
                                                if (mrr.RuleType == MailRuleType.GetLink)
                                                    if (mrr.IsMatch)
                                                    {
                                                        MailLink ml = new MailLink()
                                                        {
                                                            LetterCheckResultId = lwp.StartTime,
                                                            Link = mrr.Result,
                                                            User = user,
                                                            Password = passw
                                                        };
                                                        lwp.ListMailLink.Add(ml);
                                                    }

                                            //после того как нашли все нужные совпадения по правилам и заполнили все структуры для сохранения в базу 
                                            //помечаем письмо для удаления, оно будет удалено после того как освободиться обьект Pop3Client client
                                            client.DeleteMessage(iMessage);
                                            lwp.Result = Consts.TRUE;
                                            lwp.MessageContent = mContent;

                                            WorkWithDbData db = new WorkWithDbData(_tprm.ConnectionString, _tprm.Log);
                                            db.UpdateMailCheckStatus(_tprm.MailParams.MailAccountForCheck.Id, lwp);
                                        }
                                    }
                                }
                                catch (Exception ex)
                                {
                                    lwp.ResultMessage = ex.Message;
                                    WorkWithDbData db = new WorkWithDbData(_tprm.ConnectionString, _tprm.Log);
                                    db.UpdateMailCheckStatus(_tprm.MailParams.MailAccountForCheck.Id, lwp);
                                }
                            }// if (lwp.Result != Consts.TRUE)
                        }//for (int iLetters = 0; iLetters < _tprm.MailParams.LettersWorkParam.Count; iLetters++)
                    }//for (int iMessage = 1; iMessage <= messageCount; iMessage++)  
                }
            }
            catch (Exception ex)
            {
                WriteLog(String.Format("Error of Check Mail for account [{0}][{1}]: {2}{3}"
                    , _tprm.MailParams.MailAccountForCheck.Id
                    , _tprm.MailParams.MailAccountForCheck.Mail
                    , Environment.NewLine
                    , ex.Message), MessageType.Error);
            }
            SetMailChekerResult(_tprm);
        }

        private static void GetMessage(Pop3Client client, int iMessage, ref byte[] messageContent, ref Message message)
        {
            if (message == null)
            {
                messageContent = client.GetMessageAsBytes(iMessage);
                message = new Message(messageContent);
            }
        }

        private static bool CheckSubject(List<MailRuleResult> mailRules, Pop3Client client, int iMessage, MailRuleType mrt)
        {
            bool isMatch = false;
            foreach (MailRuleResult mrr in mailRules)
                if (mrr.RuleType == mrt && !mrr.IsMatch)
                {
                    MessageHeader headers = client.GetMessageHeaders(iMessage);
                    Regex re = new Regex(mrr.Rule);
                    string subj = headers.Subject;
                    mrr.IsMatch = re.IsMatch(subj);
                    if (mrr.IsMatch)
                    {
                        mrr.Result = re.Match(headers.Subject).Groups[1].Value;
                        isMatch = true;
                    }
                }

            return isMatch;
        }


        private static bool CheckBody(List<MailRuleResult> mailRules, Message message, MailRuleType mrt)
        {
            bool isMatch = false;
            foreach (MailRuleResult mrr in mailRules)
                if (mrr.RuleType == mrt && !mrr.IsMatch)
                {
                    List<MessagePart> textVersions = message.FindAllTextVersions();
                    foreach (MessagePart textVersion in textVersions)
                    {
                        string body = textVersion.GetBodyAsText();
                        Regex re = new Regex(mrr.Rule);
                        mrr.IsMatch = re.IsMatch(body);
                        if (mrr.IsMatch)
                        {
                            mrr.Result = re.Match(body).Groups[1].Value;
                            isMatch = true;
                            break;
                        }
                    }
                }

            return isMatch;
        }

        /// <summary>
        /// проверяет есть ли результат по правилу
        /// </summary>
        /// <param name="mailRules"></param>
        /// <param name="rt"></param>
        /// <returns></returns>
        private static bool IsMatchExists(List<MailRuleResult> mailRules, MailRuleType rt)
        {
            string matchResult = "";
            return FirstMatch(mailRules, rt, out matchResult);
        }

        /// <summary>
        /// Проверяет был ли найден результат по правилу
        /// Возвращает первый результат по такому типу правила
        /// </summary>
        /// <param name="mailRules"></param>
        /// <param name="rt"></param>
        /// <param name="matchResult"></param>
        /// <returns></returns>
        private static bool FirstMatch(List<MailRuleResult> mailRules, MailRuleType rt, out string matchResult)
        {
            bool result = false;
            matchResult = "";
            foreach (MailRuleResult mrr in mailRules)
                if (mrr.RuleType == rt)
                    if (mrr.IsMatch)
                    {
                        result = true;
                        matchResult = mrr.Result;
                        break;
                    }

            return result;
        }

        /// <summary>
        /// проверяет нужно ли выполнять правило
        /// </summary>
        /// <param name="mailRules"></param>
        /// <param name="rt"></param>
        /// <returns></returns>
        private static bool IsNeedCheckRule(List<MailRuleResult> mailRules, MailRuleType rt)
        {
            bool result = false;
            foreach (MailRuleResult mrr in mailRules)
                if (mrr.RuleType == rt)
                {
                    result = true;
                    break;
                }
            return result;
        }

        private void SetMailChekerResult(TaskParams tprm)
        {
            if (_onChekerResult != null)
                _onChekerResult(tprm);
        }

        private void WriteLog(string message, MessageType mt = MessageType.Info)
        {
            if (_tprm.Log != null)
                _tprm.Log(message, mt);
        }

    }



}
